/****************************************************************************
 * Elisa 1.0                                                                *
 *--------------------------------------------------------------------------*
 * Author: Laurent Granvilliers, Vincent Sorin                              *
 * Copyright (c) 2004 Laboratoire d'Informatique de Nantes Atlantique       *
 *--------------------------------------------------------------------------*
 * Elisa is distributed WITHOUT ANY WARRANTY. Read the associated           *
 * COPYRIGHT file for more details.                                         *
 *--------------------------------------------------------------------------*
 * elisa_real_var.cpp                                                       *
 ****************************************************************************/

#include "elisa_real_var.h"
#include <iostream>

USING_ELISA
int
RealVar::DefaultDigits = 8;


//--------------------------------------------------------------------------
// class RealVar
//--------------------------------------------------------------------------

void RealVar::setPrecision(double p)
{
	if (m_implementation != 0) {
		(static_cast<RealVarImpl *>(m_implementation.get()))->setPrecision(p);
	}
}

void RealVar::SetPrecision(double p)
{
	RealVarImpl::SetPrecision(p);
}

Variable *RealVar::copy(SearchTree *searchTree) const
{
	RealVar *v = new RealVar(this->domain(), this->m_name);
	v->buildImplementation(searchTree);
	return v;
}

void RealVar::buildImplementation(SearchTree *searchTree)
{
	m_implementation = VarImplPtr(new RealVarImpl(this, searchTree, this->domain()));
}

//--------------------------------------------------------------------------
// class RealVarImplementation
//--------------------------------------------------------------------------

double RealVarImpl::DefaultPrecision = 1.0e-8;

size_t RealVarImpl::size() const
{
	return sizeof(Interval);
}

Interval RealVarImpl::domain() const
{
	return VariableImpl::const_domain<Interval>();
}

Interval RealVarImpl::domain(Box *b) const
{
	return VariableImpl::const_domain<Interval>(b);
}

void RealVarImpl::setEmptyDomain()
{
	VariableImpl::setDomain<Interval>(Interval::EmptySet);
	VariableImpl::notifyChange(DomainReduction);
}

void RealVarImpl::setDomain(const Interval & I)
{
	if (*VariableImpl::domain<Interval>() != I) {
		VariableImpl::setDomain<Interval>(I);
		VariableImpl::notifyChange(DomainReduction);
	}
}

void RealVarImpl::setDomain(const Interval & I, Box *b)
{
	if (*VariableImpl::domain<Interval>(b) != I) {
		VariableImpl::setDomain<Interval>(I, b);
		VariableImpl::notifyChange(DomainReduction);
	}
}

void RealVarImpl::setInterDomain(const Interval & i)
{
	Interval i1 = VariableImpl::const_domain<Interval>();
	Interval i2(i1);
	if (i1.intersect(i) != i2) {
		VariableImpl::setDomain<Interval>(i1); // modified domain
		VariableImpl::notifyChange(DomainReduction); // call of listeners
	}
}

void RealVarImpl::setQuietDomain(const Interval & I)
{
	VariableImpl::setDomain<Interval>(I);
}

void RealVarImpl::setQuietDomain(const Interval & I, Box *b)
{
	VariableImpl::setDomain<Interval>(I, b);
}


void RealVarImpl::saveDomain()
{
	m_Isave = VariableImpl::const_domain<Interval>();
}

void RealVarImpl::restoreDomain()
{
	VariableImpl::setDomain<Interval>(m_Isave);
}

void RealVarImpl::setPrecision(double p)
{
	m_precision = p;
}

double RealVarImpl::precision() const
{
	return m_precision;
}


bool RealVarImpl::isSplittable() const
{
	return((m_precision == 0.0) ?
	       !(const_domain<Interval>().isCanonical()) :
	       !(const_domain<Interval>().isPrecise(m_precision)));
}

void RealVarImpl::split() const
{
	// Split of domain in I and J
	//Interval I, J;
	//VariableImpl::const_domain<Interval>().split(I, J);
	Interval::Pair r;
	r = VariableImpl::const_domain<Interval>().split();
	VariableImpl::splitBox<Interval>(r.first, r.second);
}

// TODO
void RealVarImpl::split3() const
{
	// Split of domain in 3
	Interval intA[3];

	/*
	Interval d = VariableImpl::const_domain<Interval>();

	double a = std::max<Interval::Bound>(d.inf(), Interval::Universe.ing()),
		b = std::min<Interval::Bound>(Interval::Universe.sup(), d.sup());

	if (a==b) {    // [r,r] [-oo,MinReal], [MaxReal,+oo]
		intA[0] = Interval(a, a);
		intA[1] = Interval(a, a);
		intA[2] = Interval(a, a);
	}
	else if (nextUp(a) == b) {  // [r,r+], [-oo,succ MinReal], [pred MaxReal,+oo]
		if (inf(d) == Icode::one_plus_infinity) { // ?
			intA[0] = Interval::Universe;
			intA[1] = Interval::Universe;
			intA[2] = Interval::Universe;
		}
		else if (d.sup() == -Icode::one_plus_infinity) {
			intA[0] = Interval::Universe;
			intA[1] = Interval::Universe;
			intA[2] = Interval::Universe;
		}
		else {
			intA[0] = Interval(a, a);
			intA[1] = Interval(a, a);
			intA[2] = Interval(a, b);
		}
	}
	else { // [r,s], s> succ r
		double w = (b-a)/3;
		double c = a + w;
		if ((c>a) && (c<b)) 
			intA[0] = Interval(a, c);
		else
			intA[0] = Interval(a, a);
		c += w;
		if ((c>a) && (c<b)) 
			intA[1] = Interval(a, c);
		else
			intA[1] = Interval(intA[0].sup(), b);
		c += w;
		if ((c>a) && (c<b)) 
			intA[2] = Interval(c, b);
		else
			intA[2] = Interval(intA[1].sup(), b);
	}
	*/

	VariableImpl::splitBox<Interval>(intA, 3);
}

void RealVarImpl::toPoint(Box *b) const
{
	Interval i(this->domain(b));
	Interval::Bound m(i.mid());
	VariableImpl::setDomain<Interval>(Interval(m), b);
}


void RealVarImpl::SetPrecision(double p)
{
	DefaultPrecision = p;
}

void RealVarImpl::updateSymbolicDomain() const
{
	*dynamic_cast<Interval *>(m_symbolicRep->getDomain()) = this->domain();
}

//--------------------------------------------------------------------------
// Class VecRealVar
//--------------------------------------------------------------------------
void VecRealVar::VecVarName(char *c,
                            const char *init,
                            int n)
{
	int i = 0;
	char ci[10];

	/// Computation in ci of the inverse of n (eg 102 -> 201)
	if (n == 0) {
		ci[i] = '0';
		ci[i + 1] = '\0';
	} else {
		while (n > 0) {
			ci[i++] = '0' + (n % 10);
			n /= 10;
		}
		ci[i] = '\0';
	}

	/// Computation of the variable name in c
	unsigned int j = 0;
	for (; j < strlen(init); ++j) {
		c[j] = init[j];
	}
	for (int k = (int)strlen(ci) - 1; k >= 0; --k, ++j) {
		c[j] = ci[k];
	}
	c[j] = '\0';
}

VecRealVar::VecRealVar(int n,
                       const Interval & I,
                       const char *name)
{
	m_vars = new RealVar *[m_n = n];
	char s[10];
	for (int i = m_n - 1; i >= 0; --i) {
		VecVarName(s, name, i + 1);
		m_vars[i] = new RealVar(I, s);
	}
	m_first = 0;
}

VecRealVar::VecRealVar(int first,
                       int n,
                       const Interval & I,
                       const char *name)
{
	m_vars = new RealVar *[m_n = n];
	char s[10];
	for (int i = m_n - 1; i >= 0; --i) {
		VecVarName(s, name, i + first);
		m_vars[i] = new RealVar(I, s);
	}
	m_first = first;
}

VecRealVar::~VecRealVar()
{
	for (int i = 0; i < m_n; ++i) {
		delete m_vars[i];
	}
	delete[] m_vars;
}

int VecRealVar::size() const
{
	return m_n;
}

RealVar &
VecRealVar::operator[](int i) const
throw (VecRealVar::OutOfRange)
{
	if ((i >= m_first) && (i <= m_n + m_first - 1)) {
		return *(m_vars[i - m_first]);
	} else {
		throw OutOfRange();
	}
}

void VecRealVar::setPrecision(double p)
{
	for (int i = 0; i < m_n; ++i) {
		m_vars[i]->setPrecision(p);
	}
}

void VecRealVar::setDigits(int n)
{
	for (int i = 0; i < m_n; ++i) {
		m_vars[i]->setDigits(n);
	}
}

